import axios, {
	AxiosError,
	AxiosInstance,
	AxiosResponse,
	Canceler,
	InternalAxiosRequestConfig,
	AxiosRequestConfig,
} from 'axios'
import { errorCodeType } from '@/utils/http/error-code-type'
import { lStorage } from '../storage'
import { store } from '@/store'
import router, { resetRouter } from '@/router'
import NProgress from 'nprogress'
import { ElMessage, ElMessageBox } from 'element-plus'
import { errorHandler, ErrorType } from '@/utils/errorHandler'

// 扩展请求配置类型
declare module 'axios' {
	interface InternalAxiosRequestConfig {
		duplicateRequestValidation?: boolean // 是否开启重复请求校验
		duplicateRequestValidationTime?: number // 重复请求校验时间
		isToken?: boolean // 是否使用token验证
		requestId?: string // 请求唯一标识
		showLoading?: boolean // 是否显示加载状态
		showErrorMessage?: boolean // 是否显示错误信息
		errorMessageMode?: 'message' | 'notification' | 'none' // 错误提示方式
	}
}

// 请求队列项类型
interface RequestQueueItem {
	url: string
	method: string
	createTime: number
	params: unknown
	data: unknown
	cancel: Canceler | null
	requestId: string
}

// HTTP响应数据结构
export interface ApiResponse<T = any> {
	code: number
	data: T
	message?: string
	msg?: string
	success?: boolean
	timestamp?: number
}

// 配置类型
interface HttpConfig {
	timeout: number
	baseURL: string
	successCodes: (number | string)[]
	tokenExpiredCodes: (number | string)[]
	duplicateValidationTime: number
}

// 生成请求唯一标识
const generateRequestId = (config: InternalAxiosRequestConfig): string => {
	const { url, method, params, data } = config
	return `${method}:${url}:${JSON.stringify(params)}:${JSON.stringify(data)}`
}

/**
 * 安全的状态码比较函数
 * @param code 响应状态码
 * @param targetCodes 目标状态码数组
 * @returns 是否匹配
 */
const isCodeMatch = (code: any, targetCodes: (number | string)[]): boolean => {
	if (code === undefined || code === null) return false

	// 将code转换为字符串和数字进行比较
	const codeStr = String(code)
	const codeNum = Number(code)

	return targetCodes.some((targetCode) => {
		const targetStr = String(targetCode)
		const targetNum = Number(targetCode)

		// 字符串比较或数字比较
		return (
			codeStr === targetStr ||
			(!isNaN(codeNum) && !isNaN(targetNum) && codeNum === targetNum)
		)
	})
}

export class HttpInterceptors {
	private requestQueue: RequestQueueItem[] = []
	private instance: AxiosInstance
	private isHandlingTokenExpiration = false
	private config: HttpConfig

	constructor() {
		// 可配置的HTTP设置
		this.config = {
			timeout: Number(import.meta.env.VITE_APP_DEFAULT_HTTP_TIMEOUT) || 10000,
			baseURL:
				import.meta.env.VITE_APP_USE_MOCK === 'true'
					? ''
					: import.meta.env.VITE_APP_BASE_API,
			// 支持字符串和数字类型的成功状态码
			successCodes: [0, 200, 1, 1000, 2000, '0', '200', '1', '1000', '2000'],
			// 支持字符串和数字类型的token过期状态码
			tokenExpiredCodes: [10021, 401, '10021', '401'],
			duplicateValidationTime:
				Number(import.meta.env.VITE_APP_PREVENT_DUPLICATE_SUBMISSIONS) || 1000,
		}

		this.instance = axios.create({
			baseURL: this.config.baseURL,
			timeout: this.config.timeout,
		})

		this.init()
	}

	private init() {
		this.setupRequestInterceptor()
		this.setupResponseInterceptor()
		// 定期清理过期请求
		setInterval(() => this.cleanExpiredRequests(), 30000)
	}

	private setupRequestInterceptor() {
		this.instance.interceptors.request.use(
			(config: InternalAxiosRequestConfig) => {
				// 设置默认值
				config.isToken = config.isToken !== false
				config.duplicateRequestValidation =
					config.duplicateRequestValidation ?? true
				config.duplicateRequestValidationTime =
					config.duplicateRequestValidationTime ??
					this.config.duplicateValidationTime
				config.showLoading = config.showLoading !== false
				config.showErrorMessage = config.showErrorMessage !== false
				config.errorMessageMode = config.errorMessageMode || 'message'

				// 显示加载状态
				if (config.showLoading) {
					NProgress.start()
				}

				// 添加token
				if (config.isToken) {
					const token = lStorage.get('HM_USER_TOKEN')
					if (token) {
						config.headers['token'] = token
						config.headers['Authorization'] = `Bearer ${token}`
					} else {
						this.handleError(ErrorType.PERMISSION, '用户未登录，请先登录')
						return Promise.reject(new Error('已退出登录，请重新登录！'))
					}
				}

				// 生成请求唯一标识
				config.requestId = generateRequestId(config)

				// 处理重复请求
				if (config.duplicateRequestValidation) {
					this.handleDuplicateRequest(config)
				}

				return config
			},
			(error: AxiosError) => {
				NProgress.done()
				this.handleRequestError(error)
				return Promise.reject(error)
			}
		)
	}

	private setupResponseInterceptor() {
		this.instance.interceptors.response.use(
			(response: AxiosResponse) => {
				NProgress.done()
				this.removeRequestFromQueue(response.config)

				const res = response.data

				// 处理token失效 - 使用安全的状态码比较
				if (isCodeMatch(res.code, this.config.tokenExpiredCodes)) {
					this.handleTokenExpiration()
					return Promise.reject(new Error('页面已失效，请重新登录'))
				}

				// 处理非成功响应 - 使用安全的状态码比较
				if (!isCodeMatch(res.code, this.config.successCodes)) {
					const errorMessage = res.msg || res.message || '系统出错'
					this.handleError(ErrorType.BUSINESS, errorMessage, res.code)

					if (response.config.showErrorMessage) {
						this.showErrorMessage(
							errorMessage,
							response.config.errorMessageMode
						)
					}

					return Promise.reject(res)
				}

				// 直接返回响应数据，而不是整个response对象
				return res
			},
			(error: any) => {
				NProgress.done()

				if (error.config) {
					this.removeRequestFromQueue(error.config)
				}

				this.handleResponseError(error)
				return Promise.reject(error)
			}
		)
	}

	private handleDuplicateRequest(config: InternalAxiosRequestConfig) {
		const { requestId, duplicateRequestValidationTime } = config

		// 查找重复请求
		const duplicateRequest = this.requestQueue.find(
			(item) =>
				item.requestId === requestId &&
				Date.now() - item.createTime <= (duplicateRequestValidationTime || 0)
		)

		if (duplicateRequest) {
			// 取消重复请求
			config.cancelToken = new axios.CancelToken((cancel) => {
				cancel('Duplicate request')
			})
			return
		}

		// 添加新请求到队列
		this.addRequestToQueue(config)
	}

	private addRequestToQueue(config: InternalAxiosRequestConfig) {
		const { url, method, params, data, requestId } = config

		config.cancelToken = new axios.CancelToken((cancel) => {
			this.requestQueue.push({
				url: url!,
				method: method!,
				createTime: Date.now(),
				params: params || {},
				data: data || {},
				cancel,
				requestId: requestId!,
			})
		})
	}

	private removeRequestFromQueue(config?: InternalAxiosRequestConfig) {
		if (!config?.requestId) return

		this.requestQueue = this.requestQueue.filter((item) => {
			if (item.requestId === config.requestId) {
				item.cancel = null
				return false
			}
			return true
		})
	}

	private cleanExpiredRequests() {
		const now = Date.now()
		this.requestQueue = this.requestQueue.filter((item) => {
			const isExpired = now - item.createTime > this.config.timeout
			if (isExpired && item.cancel) {
				item.cancel('Request timeout')
			}
			return !isExpired
		})
	}

	private handleTokenExpiration() {
		if (this.isHandlingTokenExpiration) return
		this.isHandlingTokenExpiration = true

		ElMessageBox.confirm('当前页面已失效，请重新登录', '提示', {
			confirmButtonText: '确定',
			showCancelButton: false,
			type: 'warning',
		})
			.then(async () => {
				try {
					// 清除所有状态
					store.useUserStore().clearUserData()
					store.useTabsStore().clearTabs()
					store.useRoutesStore().clearRoutes(router)
					resetRouter()

					// 清除本地存储
					lStorage.remove('HM_USER_TOKEN')
					lStorage.remove('HM_USER_ROUTES')

					// 重定向到登录页
					await router.push('/login')
				} catch (error) {
					console.error('登出处理失败:', error)
					// 强制刷新页面作为兜底方案
					window.location.href = '/login'
				} finally {
					this.isHandlingTokenExpiration = false
				}
			})
			.catch(() => {
				this.isHandlingTokenExpiration = false
			})
	}

	private handleError(type: ErrorType, message: string, code?: number) {
		errorHandler.reportCustomError(type, message, code)
	}

	private showErrorMessage(message: string, mode?: string) {
		switch (mode) {
			case 'none':
				break
			case 'notification':
				ElMessage({
					message,
					type: 'error',
					duration: 5000,
				})
				break
			case 'message':
			default:
				ElMessage.error(message)
				break
		}
	}

	private handleRequestError(error: any) {
		this.handleError(ErrorType.NETWORK, error.message)

		if (error.response?.data) {
			const responseData = error.response.data as any
			if (
				responseData.code &&
				isCodeMatch(responseData.code, this.config.tokenExpiredCodes)
			) {
				this.handleTokenExpiration()
			}
		}
	}

	private handleResponseError(error: any) {
		let message = error.message || '请求失败'

		if (axios.isCancel(error)) {
			if (message === 'Duplicate request') {
				this.showErrorMessage('请勿重复提交', 'message')
			}
			return
		}

		// 根据错误类型设置不同的错误信息
		if (message.includes('timeout')) {
			message = '请求超时，请检查网络连接'
			this.handleError(ErrorType.NETWORK, message)
		} else if (error.response?.status) {
			message = errorCodeType(error.response.status)
			this.handleError(ErrorType.NETWORK, message, error.response.status)
		} else if (message === 'Network Error') {
			message = '网络连接错误，请检查网络'
			this.handleError(ErrorType.NETWORK, message)
		} else {
			this.handleError(ErrorType.RUNTIME, message)
		}

		this.showErrorMessage(message, 'message')
	}

	getInterceptors(): AxiosInstance {
		return this.instance
	}

	// 更新配置
	updateConfig(newConfig: Partial<HttpConfig>) {
		this.config = { ...this.config, ...newConfig }

		// 如果更新了baseURL或timeout，需要重新设置axios实例
		if (newConfig.baseURL !== undefined) {
			this.instance.defaults.baseURL = newConfig.baseURL
		}
		if (newConfig.timeout !== undefined) {
			this.instance.defaults.timeout = newConfig.timeout
		}
	}

	// 添加成功状态码
	addSuccessCode(...codes: (number | string)[]) {
		this.config.successCodes.push(...codes)
		// 去重
		this.config.successCodes = [...new Set(this.config.successCodes)]
	}

	// 添加token过期状态码
	addTokenExpiredCode(...codes: (number | string)[]) {
		this.config.tokenExpiredCodes.push(...codes)
		// 去重
		this.config.tokenExpiredCodes = [...new Set(this.config.tokenExpiredCodes)]
	}

	// 获取当前配置
	getConfig(): HttpConfig {
		return { ...this.config }
	}
}
